#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>

#include "guess.h"

#ifdef _TEST
#undef _TEST
#include "world.c"
#define _TEST
#endif

struct GuessState
{
    int width;
    int height;
    int objs;
    char ** all_possable_world;
    char * world_array;
};

GuessState * CreateGuessState(int width, int height, int objs, int level)
{
    GuessState * stat = malloc(sizeof(GuessState)); 
    if (stat == 0) {
        return 0;
    }

    stat->width = width;
    stat->height = height;
    stat->objs = objs;
    stat->all_possable_world = 0;
    stat->world_array = 0;
    return stat;
}


typedef int (*callback)(World * w, int x, int y, void * data);

int for_each(World * w, callback cb, void * data)
{
    int x, y;
    for (y = 0; y < w->height; y++) {
        for (x = 0; x < w->width; x++) {
            if (cb(w, x, y, data) < 0) {
                return -1;
            }
        }
    }
    return 0;
}

int for_each_next(World * w, callback cb, void * data,
        int next_x, int next_y)
{
    int x, y;
    for (y = next_y; y < w->height; y++) {
        for (x = next_x; x < w->width; x++) {
            if (cb(w, x, y, data) < 0) {
                return -1;
            }
        }
        next_x = 0;
    }
    return 0;
}

int Add(World * w, int x, int y, void * data)
{
    int objs = (int)(long)data;
    if (objs == 0) {
        char * p = DumpWorld(w, 0);
        printf("%s\n", p);
        free(p);
        return 0;
    }
    int d;
    for (d = 0; d < 4; d++) {
        if (CreateObject(w, x, y, d) != 0) {
            continue;
        }
        for_each_next(w, Add, (void*)(long)(objs-1), x+1, y);
        RemoveObjectAt(w, x ,y);
    }
    return 0;
}

char ** LoadAllWorld(int width, int height, int objs)
{
    World * w = CreateWorld(width, height, 0);
    for_each(w, Add, (void*)(long)objs);
    DestroyWorld(w);
    return 0; 
}

int DoGuess(GuessState * cur_stat, int * x, int * y)
{
    if (cur_stat->all_possable_world == 0) {
        cur_stat->all_possable_world = LoadAllWorld(cur_stat->width,
                                                    cur_stat->height,
                                                    cur_stat->objs);
    }
    if (cur_stat->world_array == 0) {
        cur_stat->world_array = malloc(sizeof(char) * cur_stat->width * cur_stat->height);
        memset(cur_stat->world_array, 0, sizeof(char) * cur_stat->width * cur_stat->height);
    }

    //TODO:
    return -1;
}

char possable[66816][101];
void FillHead(int head[100])
{
    int i, j;
    for (i = 0; i < 66816; i++) {
        if ((possable[i][100]) == -1) {
            continue;
        }

        for (j = 0; j < 100; j++) {
            if (possable[i][j] == '2') {
                head[j]++;
            }
        }
    }
}

int CheckMode(char mode[101], char world_array[10][10])
{
    int x, y;
    for (y = 0; y < 10; y++) {
        for (x = 0; x < 10; x++) {
            int pos = y * 10 + x;
            if (world_array[y][x] < 0) {
                continue;
            }

            if ((mode[pos] - '0') != world_array[y][x]) {
                mode[100] = -1;
                return -1;
            }
        }
    }
    return 0;
}

void UpdatePossable(char world_array[10][10])
{
    int i;
    for (i = 0; i < 66816; i++) {
        if (possable[i][100] == -1) {
            continue;
        }
        if (CheckMode(possable[i], world_array)) {
            possable[i][100] = -1;
        }
    }
}

int RandMaxPos(int head[100], char world_array[10][10])
{
    int pos[100] = {-1};

    int i;
    int iMaxCnt = 0;
    int iMax = 0;
    for (i = 0; i < 100; i++) {
        int x = i % 10;
        int y = i / 10;
        if (world_array[y][x] >= 0) { continue; }
        if (iMax < head[i]) {
            iMax = head[i];
            iMaxCnt = 1;
            pos[iMaxCnt-1] = i;
        } else if (head[i] == iMax) {
            iMaxCnt ++;
            pos[iMaxCnt-1] = i;
        }
    }

    int sed = rand() % iMaxCnt;
    i = pos[sed];
    printf("iMax: %d iMaxCnt %d, use %d\n", iMax, iMaxCnt, i);
    return i;
}

int GetNextPosition(char world_array[10][10], int * x, int * y)
{
    int head[100] = {0};
    UpdatePossable(world_array);
    FillHead(head);
    int pos = RandMaxPos(head, world_array);
    *x = pos % 10;
    *y = pos / 10;
    return 0;
};

void LoadPossable(int width, int height)
{
    int i;
    char file[256] = {0};
    sprintf(file, "./%dx%d.txt", width, height);
    FILE * f = fopen(file, "rb");
    assert(f && "open file failed");

    for (i = 0;  i < 66816; i++) {
        if (fread(possable[i], 1, 101, f) != 101) {
            assert(0 && "read data failed");
            exit(0);
        }
    }

    for (i = 0; i < 66816; i++) {
        possable[i][100] = 0;
    }
}

void InitWorldArray(char world_array[10][10])
{
    int i, j;
    for(i = 0; i < 10; i++) {
        for(j = 0; j < 10; j++) {
            world_array[i][j] = -1;
        }
    }
}

void Guess2(World * w)
{
    LoadPossable(w->width, w->height);
    char world_array[10][10];
    InitWorldArray(world_array);

    int iHit = 0;
    int iStep = 0;
    while(iHit < 3) {
        int x, y;
        GetNextPosition(world_array, &x, &y);
        int ret = AttackWorld(w, x, y);
        iStep ++;
        world_array[y][x] = ret;
        printf("step %d, hit %d, %d\n", iStep, x, y);
        DrawWorld(w);

        if (ret == 2) {
            iHit++;
        }
    }
}

#ifdef _TEST
int main(int argc, char * argv[])
{
    LoadAllWorld(10, 10, 3);
    return 0;
    int x = 10;
    int y = 10;
    if (argc == 3) {
        x = atoi(argv[1]);
        y = atoi(argv[2]);
    }

    World * w = CreateWorld(x, y, 3);
    printf("=============== 10 x 10 ================\n");

    srand(time(0));

    int iObj = 0;
    while (iObj < 3) {
        int x = rand() % 10;
        int y = rand() % 10;
        int d = rand() % 4;

        if (CreateObject(w, x, y, d) == 0) {
            continue;
        }
        iObj++;
    }

    DrawWorld(w);

    Guess2(w);

    return 0;
}
#endif
